package com.eg366.nioLearn.baseSocket.chapter05;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 * 回显协议实现
 * 
 * @author eg366
 * 
 */
public class EchoSelectorProtocol implements TcpProtocol {

	private int bufSize;

	public EchoSelectorProtocol(int bufsize) {
		this.bufSize = bufsize;
	}

	@Override
	public void handleAccept(SelectionKey key) throws IOException {
		/* 从键中获取通道，并接收连接 */
		SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept();
		/* 设置非阻塞 */
		clntChan.configureBlocking(false);
		/* 设置选择器，第三个参数 将作为一个附件传递给register方法，与对应的SelectionKey进行关联 */
		clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));
	}

	@Override
	public void handleRead(SelectionKey key) throws IOException {
		/* 获取关联的通道 */
		SocketChannel clntChan = (SocketChannel) key.channel();
		/* 获取与键SelectionKey关联的缓冲区 */
		ByteBuffer buf = (ByteBuffer) key.attachment();
		/* 从通道中读取数据 */
		int bytesRead = clntChan.read(buf);
		/* 返回-1 标示底层连接已关闭，此时关闭通道 */
		if (bytesRead == -1) {
			clntChan.close();
		} else if (bytesRead > 0) {
			/* 如果接受数据完整，标记为可写 */
			key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
		}
	}

	@Override
	public void handleWrite(SelectionKey key) throws IOException {
		/* 获取与键SelectionKey关联已包含数据的缓冲区(之前在handleRead方法中已从通道中读取过数据) */
		ByteBuffer buf = (ByteBuffer) key.attachment();
		/* 准备缓冲区的写操作 */
		buf.flip();
		/* 获取通道 */
		SocketChannel clntChan = (SocketChannel) key.channel();
		/* 向通道写数据 */
		clntChan.write(buf);
		/* 如果缓冲区为空，则标记为不再写数据 */
		if(!buf.hasRemaining()) {
			key.interestOps(SelectionKey.OP_READ);
		}
		/* 压缩缓冲区，如果缓冲区还有数据，该操作将其移动到缓冲区的前端，以使下次迭代能够读入更多数据 */
		buf.compact();
	}

}
